package com.paper.tiger.easy.rpc.s.client.client;
import com.paper.tiger.easy.rpc.core.Stub;
import io.netty.channel.Channel;

import com.paper.tiger.easy.rpc.core.ExceptionHandler;
import com.paper.tiger.easy.rpc.core.RegisterData;
import com.paper.tiger.easy.rpc.core.codec.KryoDecoder;
import com.paper.tiger.easy.rpc.core.codec.KryoEncoder;
import com.paper.tiger.easy.rpc.s.client.SpringContextSupport;
import com.paper.tiger.easy.rpc.s.client.config.Config;
import com.paper.tiger.easy.rpc.s.client.registy.common.ServiceMessageData;
import com.paper.tiger.easy.rpc.s.client.server.InvokeServerHeartBeatHandler;
import com.paper.tiger.easy.rpc.s.client.handler.KryoDoveDecoder;
import com.paper.tiger.easy.rpc.s.client.handler.KryoDoveEncoder;
import com.paper.tiger.easy.rpc.s.client.registy.common.RegisterConfig;
import com.paper.tiger.easy.rpc.s.client.registy.handler.ClientHeartBeatHandler;
import com.paper.tiger.easy.rpc.s.client.registy.handler.ClientServiceHandler;
import com.paper.tiger.easy.rpc.s.client.server.InvokeServiceHandler;
import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.timeout.IdleStateHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.net.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

/**
 *
 * @version 1.0, 2021/6/18 09:03
 * @since JDK 1.8
 */
public class ContextManager
{

    private static Logger logger = LoggerFactory.getLogger(ContextManager.class);

    private EventLoopGroup group ;

    /**
     * 通向服务端的channel
     */
    private Map<String, List<LetterService>> doveClientChannelMap = new ConcurrentHashMap<>();

    /**
     * 缓存了本地通向客户端的Channel
     */
    private Map<String, List<LetterService>> doveServerChannelMap = new ConcurrentHashMap<>();

    private Channel registyChannel;

    private Config config;

    private Bootstrap registyBootStrap;

    private Integer doveServerPort;

    private String localIp;

    private ContextManager(){

        new RegisterConfig().register();

        config = SpringContextSupport.getBean(Config.class);

        doveServerPort = generatePort();
        localIp = localIp();
        group = new NioEventLoopGroup(config.getDoveConnectionNum());

        startRegistyClient();
        startDoveServer();

    }

    private static ContextManager manager = new ContextManager();

    public static ContextManager getInstance(){
        return manager;
    }


    public Integer getDoveServerPort()
    {
        return doveServerPort;
    }

    public void setDoveServerPort(Integer doveServerPort)
    {
        this.doveServerPort = doveServerPort;
    }

    public String getLocalIp()
    {
        return localIp;
    }

    public void setLocalIp(String localIp)
    {
        this.localIp = localIp == null ? null : "".equals(localIp.trim()) ? null : localIp.trim();
    }

    public Channel getRegistyChannel()
    {
        return registyChannel;
    }

    public void setRegistyChannel(Channel registyChannel)
    {
        this.registyChannel = registyChannel;
    }



    private String localIp()
    {
        try
        {
            InetAddress addr = InetAddress.getLocalHost();

            return addr.getHostAddress();

        }
        catch (UnknownHostException e)
        {
            e.printStackTrace();
        }
        throw new RuntimeException("获取本地ip失败");
    }

    private int generatePort()
    {
        // 生成端口，从65535，开始向下生成端口

        for (int i = 65535; i >0; i--)
        {
            try
            {
                ServerSocket serverSocket = new ServerSocket(i);
                serverSocket.close();
                return i;
            }
            catch (IOException e)
            {
                continue;
            }

        }
        throw new RuntimeException("没有找到可用的端口");
    }




    public Channel clientChannel(String serviceName)
    {

        List<LetterService> letterServices = doveClientChannelMap.get(serviceName);
        if (letterServices == null){
            startDoveClient(serviceName);
        }
        letterServices = doveClientChannelMap.get(serviceName);

        // todo 需要负载均衡
        LetterService letterService = letterServices.get(0);
        return letterService.getChannels().get(0);
    }


    public void addClientChannel(String serviceName,String ip, Integer port, Channel channel)
    {
        List<LetterService> letterServices = doveClientChannelMap.get(serviceName);

        // 1. 里面没有当前服务的存根
        if (letterServices == null){
            letterServices = new ArrayList<>();
            doveClientChannelMap.put(serviceName,letterServices);
        }
        LetterService contain = new LetterService();
        contain.setIp(ip);
        contain.setPort(port);
        // 没有当前 ip：port 的
        if (!letterServices.contains(contain)){
            contain.setChannels(new ArrayList<>());
            letterServices.add(contain);
        }


        if (letterServices != null && letterServices.size() >0){

            for (LetterService letterService : letterServices)
            {
                if (letterService.getIp().equals(ip) && letterService.getPort().equals(port)){
                    letterService.getChannels().add(channel);
                }
            }
        }

    }


    public void startDoveClient(String serviceName) {

        int connectionNum = config.getDoveConnectionNum();
        List<RegisterData> dataList = ServiceMessageData.getByServiceName(serviceName);

        /**
         * 客户端需要几个连接，就新建几个连接
         */
        for (RegisterData data : dataList)
        {
            String ip = data.getIp();
            Integer port = data.getPort();
            for (int i = 0; i < connectionNum; i++)
            {
                try
                {
                    Bootstrap bootstrap = new Bootstrap();
                    bootstrap.group(group).remoteAddress(ip,port).
                            channel(NioSocketChannel.class)
                            .handler(new ChannelInitializer<SocketChannel>()
                            {
                                @Override
                                protected void initChannel(SocketChannel ch) throws Exception
                                {
                                    ChannelPipeline pp = ch.pipeline();
                                    // 发送的数据添加2为数据头
                                    pp.addLast(new LengthFieldPrepender(2));
                                    // 去除头部
                                    pp.addLast(new LengthFieldBasedFrameDecoder(0xFFFF, 0, 2, 0, 2));
                                    // 解码器 编码器
                                    pp.addLast(new KryoDoveDecoder());
                                    pp.addLast(new KryoDoveEncoder());

                                    pp.addLast(new DoveClientHeartBeatHandler());
                                    //                            pp.addLast(new CtxHanler());
                                    pp.addLast(new DoveClientServiceHandler());

                                    pp.addLast(new ExceptionHandler());

                                }
                            });

                    ChannelFuture future = bootstrap.connect().sync();
                    logger.info("客户端启动成功 - 目标服务为的信息，ip:{},port:{}",ip,port);
                    addClientChannel(data.getAppName(),ip,port,future.channel());
                }
                catch (InterruptedException e)
                {
                    e.printStackTrace();

                }

        }


        }

    }

    public void startRegistyClient()
    {
        new RegisterConfig().register();
        EventLoopGroup group = new NioEventLoopGroup(1);

        try
        {
            registyBootStrap = new Bootstrap();
            registyBootStrap.remoteAddress(new InetSocketAddress(config.getRegistyCenterHost(),
                    config.getRegistyCenterPort()))
                    .channel(NioSocketChannel.class)
                    .handler(new ChannelInitializer<SocketChannel>()
                    {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception
                        {

                            //
                            ChannelPipeline cp = ch.pipeline();
                            //

                            cp.addLast(new LoggingHandler());
                            // 剥离协议头部
                            cp.addLast(new LengthFieldBasedFrameDecoder(0xFFFF,0,2,0,2));
                            // 出栈添加协议头部
                            cp.addLast(new LengthFieldPrepender(2));

                            cp.addLast(new KryoEncoder());
                            cp.addLast(new KryoDecoder());

                            cp.addLast("logging", new LoggingHandler());
                            // 客户端发送心跳包
                            cp.addLast(new ClientHeartBeatHandler());

                            cp.addLast(new ClientServiceHandler());

                            cp.addLast(new ExceptionHandler());

                        }
                    })
                    .group(group);

            ChannelFuture future = registyBootStrap.connect().sync();
            registyChannel = future.channel();

        }
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }

    }

    public void doConnect() throws Exception
    {
        while (true){

            ChannelFuture future = registyBootStrap.connect().sync();

            if (future.isSuccess()){
                registyChannel = future.channel();
                return;
            }
            Thread.sleep(1000);
        }

    }

    public void sendRegistyMsg(RegisterData data)
    {
        if (registyChannel == null){
            throw new RuntimeException("客户端channel为空，请检查客户端是否启动成功");
        }
        data.setIp(localIp);
        data.setPort(doveServerPort);

        for (Stub stub : data.getList())
        {
            stub.setIp(localIp);
            stub.setPort(doveServerPort);
            stub.setHostKey(localIp+":"+doveServerPort);
        }
        registyChannel.writeAndFlush(data);
    }

    public void startDoveServer()
    {
        EventLoopGroup boss = new NioEventLoopGroup(config.getBossThreadNum());
        EventLoopGroup worker = new NioEventLoopGroup(config.getWorkerThreadNum());

        try
        {
            ServerBootstrap bootstrap = new ServerBootstrap();

            bootstrap.group(boss, worker).localAddress(doveServerPort).
                    channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<SocketChannel>()
                    {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception
                        {

                            ChannelPipeline pp = ch.pipeline();
                            // 发送的数据添加2为数据头
                            pp.addLast(new LengthFieldPrepender(2));
                            // 去除头部
                            pp.addLast(new LengthFieldBasedFrameDecoder(0xFFFF, 0, 2, 0, 2));

                            // 解码器 编码器
                            pp.addLast(new KryoDoveDecoder());
                            pp.addLast(new KryoDoveEncoder());

                            pp.addLast(new IdleStateHandler(config.getDoveIdeReadTimeOut(), 0, 0, TimeUnit.SECONDS));


                            pp.addLast(new InvokeServerHeartBeatHandler());

                            pp.addLast(new InvokeServiceHandler());

                            pp.addLast(new ExceptionHandler());

                        }
                    });

            ChannelFuture future = bootstrap.bind().sync();
            InetSocketAddress socketAddress = (InetSocketAddress)future.channel().localAddress();
            logger.info("服务端启动成功 - 监听端口为:{}", socketAddress.getPort());
        }
        catch (InterruptedException e)
        {
            e.printStackTrace();

        }

    }

    public Channel serverChannel(String serviceName)
    {
        List<LetterService> letterServices = doveServerChannelMap.get(serviceName);
        if (letterServices == null || letterServices.size() == 0){
            return null;
        }
        // todo 需要负载
        LetterService letterService = letterServices.get(0);
        List<Channel> channels = letterService.getChannels();
        return channels.get(0);

    }

    /**
     * 添加
     * @param serviceName
     * @param ip
     * @param port
     * @param channel
     */
    public void addServerChannel(String serviceName,String ip,Integer port,Channel channel)
    {
        List<LetterService> letterServices = doveServerChannelMap.get(serviceName);

        // 1. 里面没有当前服务的存根
        if (letterServices == null){
            letterServices = new ArrayList<>();
            doveServerChannelMap.put(serviceName,letterServices);
        }
        LetterService contain = new LetterService();
        contain.setIp(ip);
        contain.setPort(port);
        // 没有当前 ip：port 的
        if (!letterServices.contains(contain)){
            contain.setChannels(new ArrayList<>());
            letterServices.add(contain);
        }


        if (letterServices != null && letterServices.size() >0){

            for (LetterService letterService : letterServices)
            {
                if (letterService.getIp().equals(ip) && letterService.getPort().equals(port)){
                    letterService.getChannels().add(channel);
                }
            }
        }

    }

    public synchronized void removeClientChannel(Channel channel)
    {
        doveClientChannelMap.forEach((key,list)->{
            for (LetterService letterService : list)
            {
                if (letterService.getChannels().contains(channel)){
                    letterService.getChannels().remove(channel);
                }
            }
        });

        doveClientChannelMap.forEach((key,list)->{
            if (list == null || list.size() ==0){
                doveClientChannelMap.remove(key);
            }
        });
        logger.info("服务端下线，删除对应的客户端的channel，剩余channel为：{}", doveClientChannelMap);
    }

    public void removeServerChannel()
    {
        // 当前服务下线,清空去所有客户端的 channel
        logger.info("当前服务下线,清空去所有客户端的 channel：{}", doveServerChannelMap);
        doveServerChannelMap.clear();
    }

    public Channel channel(String serviceName)
    {
        // 尝试从服务端获取channel，如果服务端没有，从客户端获取连接
        Channel channel = serverChannel(serviceName);
        if (channel == null){
            channel = clientChannel(serviceName);
        }
        return channel;

    }
}
